package logic

import (
	"cloud-disk/core/define"
	"cloud-disk/core/helper"
	"cloud-disk/core/models"
	"context"
	"fmt"
	"github.com/aliyun/aliyun-oss-go-sdk/oss"
	"os"
	"strings"
	"time"

	"cloud-disk/core/internal/svc"
	"cloud-disk/core/internal/types"

	"github.com/zeromicro/go-zero/core/logx"
)

type FileUploadPrepareLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewFileUploadPrepareLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FileUploadPrepareLogic {
	return &FileUploadPrepareLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *FileUploadPrepareLogic) FileUploadPrepare(req *types.FileUploadPrepareRequest) (resp *types.FileUploadPrepareReply, err error) {
	rp := new(models.RepositoryPool)
	has, err := l.svcCtx.Engine.Where("hash = ?", req.Md5).Get(rp)
	if err != nil {
		return nil, err
	}
	resp = new(types.FileUploadPrepareReply)
	if has {
		//秒传成功
		resp.Identity = rp.Identity
	} else {
		//分片上传
		etag, err := CosInitPartAndUpload(req.Ext, req.Name, req.Path)
		if err != nil {
			return nil, err
		}
		rp.Identity = helper.GetUUID()
		rp.Name = req.Name
		rp.Ext = req.Ext
		info, err := os.Stat(req.Path)
		if err != nil {
			return nil, err
		}
		rp.Size = info.Size()
		rp.Hash = strings.Trim(etag, "\"")
		rp.Path = define.AccessPrefix + "cloud-disk/" + req.Name + req.Ext
		_, err = l.svcCtx.Engine.Insert(rp)
		if err != nil {
			return nil, err
		}
		resp.Etag = strings.Trim(etag, "\"")
	}
	return resp, nil
}

// CosInitPartAndUpload 分片上传
func CosInitPartAndUpload(ext, name, path string) (string, error) {
	client, err := oss.New(define.Endpoint, define.OssAccessKeyId, define.OssAccessKeySecret)
	if err != nil {
		fmt.Println("Error:", err)
		os.Exit(-1)
	}
	// 填写存储空间名称
	bucketName := "mwh-cloud-disk"
	// 填写Object完整路径。Object完整路径中不能包含Bucket名称
	objectName := "cloud-disk/" + name + ext
	// 填写本地文件的完整路径。如果未指定本地路径，则默认从示例程序所属项目对应本地路径中上传文件。
	locaFilename := path
	// 获取存储空间
	bucket, err := client.Bucket(bucketName)
	if err != nil {
		fmt.Println("Error:", err)
		os.Exit(-1)
	}
	// 将本地文件分片，且分片数量指定为3
	chunks, err := oss.SplitFileByPartNum(locaFilename, 3)
	fd, err := os.Open(locaFilename)
	defer fd.Close()
	// 指定过期时间
	expires := time.Date(2149, time.January, 10, 23, 0, 0, 0, time.UTC)
	// 如果需要在初始化分片时设置请求头，请参考以下示例代码
	options := []oss.Option{
		oss.MetadataDirective(oss.MetaReplace),
		oss.Expires(expires),
		// 指定该Object被下载时的网页缓存行为。
		// oss.CacheControl("no-cache"),
		// 指定该Object被下载时的名称。
		// oss.ContentDisposition("attachment;filename=FileName.txt"),
		// 指定该Object的内容编码格式。
		// oss.ContentEncoding("gzip"),
		// 指定对返回的Key进行编码，目前支持URL编码。
		// oss.EncodingType("url"),
		// 指定Object的存储类型。
		// oss.ObjectStorageClass(oss.StorageStandard),
	}
	// 步骤1：初始化一个分片上传事件，并指定存储类型为标准存储。
	imur, err := bucket.InitiateMultipartUpload(objectName, options...)
	fmt.Println(chunks, imur)
	// 步骤2：上传分片
	var parts []oss.UploadPart
	for _, chunk := range chunks {
		fd.Seek(chunk.Offset, os.SEEK_SET)
		// 调用UploadPart方法上传每个分片。
		part, err := bucket.UploadPart(imur, fd, chunk.Size, chunk.Number)
		if err != nil {
			fmt.Println("Error:", err)
			os.Exit(-1)
		}
		parts = append(parts, part)
	}
	// 指定Object的读写权限为公共读，默认为继承Bucket的读写权限
	objectAcl := oss.ObjectACL(oss.ACLPublicRead)
	// 步骤3：完成分片上传，指定文件读写权限为公共读
	cmur, err := bucket.CompleteMultipartUpload(imur, parts, objectAcl)
	if err != nil {
		fmt.Println("Error:", err)
		os.Exit(-1)
	}
	return cmur.ETag, nil
}
